#pragma once

#include <AVUIDispatcher.h>

namespace AVUI {

class LayoutEventList;

class ContextLayoutManager : public DispatcherObject
{
public:
    DECLARE_ELEMENT(ContextLayoutManager, DispatcherObject);

    class LayoutQueue;
#pragma region Internal Methods

    ContextLayoutManager();

    void AddToSizeChangedChain(SizeChangedInfo* pInfo);
    void EnterArrange();
    void EnterMeasure();
    void ExitArrange();
    void ExitMeasure();
    static ContextLayoutManager* From(Dispatcher* pDispatcher);
    UIElement* GetLastExceptionElement() { return m_pLastExceptionElement; }
    void SetLastExceptionElement(UIElement* pE) { m_pLastExceptionElement = pE; }
    void UpdateLayout();

#pragma region Internal Methods

#pragma region Internal Properties

    LayoutQueue* get_ArrangeQueue();
    LayoutEventList* get_LayoutEvents();
    LayoutQueue* get_MeasureQueue();

#pragma endregion Internal Properties


#pragma region LayoutQueue

    class LayoutQueue : public Object
    {
    public:
        class Request;

        LayoutQueue();
        void Add(UIElement* pE);
        virtual bool canRelyOnParentRecalc(UIElement* pParent) = 0;
        virtual Request* getRequest(UIElement* pE) = 0;
        UIElement* GetTopMost();
        virtual void invalidate(UIElement* pE) = 0;
        void Remove(UIElement* pE);
        void RemoveOrphans(UIElement* pParent);
        virtual void setRequest(UIElement* pE, Request* pR) = 0;

        bool get_IsEmpty() { return m_pHead == NULL; }


        class Request : public Object
        {
        public:
            Request() : Prev(NULL) { }
            TRefCountedPtr<Request> Next;
            Request* Prev;
            TRefCountedPtr<UIElement> Target;
        };

    private:

        void _addRequest(UIElement* pE);
        TRefCountedPtr<Request> _getNewRequest(UIElement* pE);
        void _removeRequest(Request* pEntry);
        void ReuseRequest(Request* pR);

        TRefCountedPtr<Request> m_pHead;
        TRefCountedPtr<Request> m_pPocket;
        int m_pocketSize;

        static const int PocketCapacity = 0x99;
        static const int PocketReserve = 8;
    };

    class InternalArrangeQueue : public LayoutQueue
    {
    public:
        virtual bool canRelyOnParentRecalc(UIElement *pParent) AVUI_OVERRIDE;
        virtual Request * getRequest(UIElement *pE) AVUI_OVERRIDE;
        virtual void invalidate(UIElement *pE) AVUI_OVERRIDE;
        virtual void setRequest(UIElement *pE, Request *pR) AVUI_OVERRIDE;
    };

    class InternalMeasureQueue : public LayoutQueue
    {
    public:
        virtual bool canRelyOnParentRecalc(UIElement *pParent) AVUI_OVERRIDE;
        virtual Request * getRequest(UIElement *pE) AVUI_OVERRIDE;
        virtual void invalidate(UIElement *pE) AVUI_OVERRIDE;
        virtual void setRequest(UIElement *pE, Request *pR) AVUI_OVERRIDE;
    };

#pragma endregion LayoutQueue

private:

#pragma region Private Methods

    void fireLayoutUpdateEvent();
    void fireSizeChangedEvents();
    Rect getProperArrangeRect(UIElement* pElement);
    void invalidateTreeIfRecovering();
    void markTreeDirty(UIElement* pE);
    void markTreeDirtyHelper(Visual* pV);
    void NeedsRecalc();
    void OnDispatcherShutdown(Object* pSender, EventArgs* e);
    void setForceLayout(UIElement* pE) { m_pForceLayoutElement = pE; }
    static void UpdateLayoutBackground(Object* pSender, Object* pArg) { ((ContextLayoutManager*)pArg)->NeedsRecalc(); }
    static void UpdateLayoutCallback(Object* pSender, Object* pArg) { ContextLayoutManager* pManager = object_cast<ContextLayoutManager>(pArg); if(pManager != NULL) pManager->UpdateLayout(); }
 
#pragma endregion Private Methods

#pragma region Private Properties

    bool get_hasDirtiness()
    {
        if (get_MeasureQueue()->get_IsEmpty())
        {
            return !get_ArrangeQueue()->get_IsEmpty();
        }
        return true;
    }

#pragma endregion Private Properties

#pragma region Private Fields

    TRefCountedPtr<InternalArrangeQueue> m_pArrangeQueue;
    int m_arrangesOnStack;
    bool m_firePostLayoutEvents;
    TRefCountedPtr<UIElement> m_pForceLayoutElement;
    bool m_gotException;
    bool m_inFireLayoutUpdated;
    bool m_inFireSizeChanged;
    bool m_isDead;
    bool m_isInUpdateLayout;
    bool m_isUpdating;
    TRefCountedPtr<UIElement> m_pLastExceptionElement;
    TRefCountedPtr<LayoutEventList> m_pLayoutEvents;
    bool m_layoutRequestPosted;
    TRefCountedPtr<InternalMeasureQueue> m_pMeasureQueue;
    int m_measuresOnStack;
    TRefCountedPtr<EventHandler> m_pShutdownHandler;
    TRefCountedPtr<SizeChangedInfo> m_pSizeChangedChain;
    TRefCountedPtr<DispatcherOperationCallback> m_pUpdateCallback;
    TRefCountedPtr<DispatcherOperationCallback> m_pUpdateLayoutBackground;
    static const int s_LayoutRecursionLimit = 250;

#pragma endregion Private Fields

};


}; // namespace AVUI
